home *** CD-ROM | disk | FTP | other *** search
/ SuperHack / SuperHack CD.bin / CODING / WINDOWS / FILSRC.ZIP / GUI / FILEMON.C < prev    next >
Encoding:
C/C++ Source or Header  |  1996-11-24  |  17.9 KB  |  668 lines

  1. /******************************************************************************
  2. *
  3. *       FILEMON - File Monitor for Windows 95
  4. *        
  5. *        By Mark Russinovich and Bryce Cogswell
  6. *
  7. *        PROGRAM: filemon.c
  8. *
  9. *        PURPOSE: Communicates with the filemon driver to display 
  10. *        file system activity information.
  11. *
  12. ******************************************************************************/
  13. #include <windows.h>    // includes basic windows functionality
  14. #include <windowsx.h>
  15. #include <commctrl.h>   // includes the common control header
  16. #include <stdio.h>
  17. #include <string.h>
  18. #include "resource.h"
  19. #include "../vxd/ioctlcmd.h"
  20.  
  21.  
  22. // Variables/definitions for the driver that performs the actual monitoring.
  23. #define                VXD_FILE        "\\\\.\\FILEVXD.VXD"
  24. #define                VXD_NAME        "FILEVXD"
  25. static HANDLE        VxDHandle        = INVALID_HANDLE_VALUE;
  26.  
  27.  
  28. // Buffer into which driver can copy statistics
  29. char                Stats[ MAX_STORE ];
  30. // Current fraction of buffer filled
  31. DWORD                StatsLen;
  32.  
  33. // Application instance handle
  34. HINSTANCE            hInst;
  35.  
  36. // Misc globals
  37. HWND                hWndList;
  38. BOOLEAN                Capture = TRUE;
  39. BOOLEAN                Autoscroll = TRUE;
  40. BOOLEAN                Deleting = FALSE;
  41.  
  42. // For info saving
  43. TCHAR                szFileName[256];
  44. BOOLEAN                FileChosen = FALSE;
  45.  
  46. // General buffer for storing temporary strings
  47. static TCHAR        msgbuf[ 257 ];
  48.  
  49. // General cursor manipulation
  50. HCURSOR             hSaveCursor;
  51. HCURSOR             hHourGlass;
  52.  
  53. // procs
  54. long APIENTRY         MainWndProc( HWND, UINT, UINT, LONG );
  55. BOOL APIENTRY       About( HWND, UINT, UINT, LONG );
  56.  
  57. //functions
  58. BOOL                 InitApplication( HANDLE );
  59. HWND                 InitInstance( HANDLE, int );
  60. HWND                 CreateList( HWND );
  61. void                 UpdateStatistics( HWND hWnd, HWND hWndList, BOOL Clear );
  62. void                SaveFile( HWND hDlg, HWND listbox, BOOLEAN SaveAs );
  63.  
  64. /******************************************************************************
  65. *
  66. *    FUNCTION:    Abort:
  67. *
  68. *    PURPOSE:    Handles emergency exit conditions.
  69. *
  70. *****************************************************************************/
  71. void Abort( HWND hWnd, TCHAR * Msg )
  72. {
  73.     MessageBox( hWnd, Msg, "filemon", MB_OK );
  74.     PostQuitMessage( 1 );
  75. }        
  76.  
  77.  
  78. /****************************************************************************
  79. *
  80. *    FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)
  81. *
  82. *    PURPOSE:    calls initialization function, processes message loop
  83. *
  84. ****************************************************************************/
  85. int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
  86.                         LPSTR lpCmdLine, int nCmdShow )
  87. {
  88.     MSG     msg;      
  89.     HWND    hWnd;
  90.         
  91.     if ( ! InitApplication( hInstance ) )
  92.         return FALSE;     
  93.  
  94.     // initializations that apply to a specific instance 
  95.     if ( (hWnd = InitInstance( hInstance, nCmdShow )) == NULL )
  96.         return FALSE;
  97.  
  98.  
  99.     // acquire and dispatch messages until a WM_QUIT message is received.
  100.     while ( GetMessage( &msg, NULL, 0, 0 ) )  {
  101.         TranslateMessage( &msg );
  102.         DispatchMessage( &msg ); 
  103.     }
  104.     return msg.wParam;                                         
  105. }
  106.  
  107.  
  108. /****************************************************************************
  109. *
  110. *    FUNCTION: InitApplication(HANDLE)
  111. *
  112. *    PURPOSE: Initializes window data and registers window class
  113. *
  114. ****************************************************************************/
  115. BOOL InitApplication( HANDLE hInstance )
  116. {
  117.     WNDCLASS  wc;
  118.     
  119.     // Fill in window class structure with parameters that describe the
  120.     // main (statistics) window. 
  121.     wc.style            = 0;                     
  122.     wc.lpfnWndProc        = (WNDPROC)MainWndProc; 
  123.     wc.cbClsExtra        = 0;              
  124.     wc.cbWndExtra        = 0;              
  125.     wc.hInstance        = hInstance;       
  126.     wc.hIcon            = LoadIcon( hInstance, "ICON" );
  127.     wc.hCursor            = LoadCursor( NULL, IDC_ARROW );
  128.     wc.hbrBackground    = GetStockObject( LTGRAY_BRUSH ); 
  129.     wc.lpszMenuName        = "LISTMENU";  
  130.     wc.lpszClassName    = "filemonClass";
  131.     if ( ! RegisterClass( &wc ) )
  132.         return FALSE;
  133.  
  134.     return TRUE;
  135. }
  136.  
  137.  
  138. /****************************************************************************
  139. *
  140. *    FUNCTION:  InitInstance(HANDLE, int)
  141. *
  142. *    PURPOSE:  Saves instance handle and creates main window
  143. *
  144. ****************************************************************************/
  145. HWND InitInstance( HANDLE hInstance, int nCmdShow )
  146. {
  147.     HWND hWndMain;
  148.  
  149.     hInst = hInstance;
  150.  
  151.     hWndMain = CreateWindow( "filemonClass", "Win95 File Monitor", 
  152.                             WS_OVERLAPPEDWINDOW,
  153.                             CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
  154.                             NULL, NULL, hInstance, NULL );
  155.  
  156.     // if window could not be created, return "failure" 
  157.     if ( ! hWndMain )
  158.         return NULL;
  159.     
  160.     // make the window visible; update its client area; and return "success"
  161.     ShowWindow( hWndMain, nCmdShow );
  162.     UpdateWindow( hWndMain ); 
  163.     return hWndMain;      
  164. }
  165.  
  166.  
  167. /****************************************************************************
  168. *
  169. *    FUNCTION: MainWndProc(HWND, unsigned, WORD, LONG)
  170. *
  171. *    PURPOSE:  Processes messages for the statistics window.
  172. *
  173. ****************************************************************************/
  174. LONG APIENTRY MainWndProc( HWND hWnd, UINT message, UINT wParam, LONG lParam) 
  175. {
  176.     DWORD            nb;
  177.     TCHAR            Path[ 256 ];
  178.     PAINTSTRUCT        Paint;
  179.     HDC                hDC;
  180.  
  181.     switch ( message ) {
  182.  
  183.         case WM_CREATE:
  184.  
  185.             // get hourglass cursor ready
  186.             hHourGlass = LoadCursor( NULL, IDC_WAIT );
  187.  
  188.             // post hourglass icon
  189.             SetCapture(hWnd);
  190.             hSaveCursor = SetCursor(hHourGlass);
  191.  
  192.             // Create the ListBox within the main window
  193.             hWndList = CreateList( hWnd );
  194.             if ( hWndList == NULL )
  195.                 MessageBox( NULL, "List not created!", NULL, MB_OK );
  196.  
  197.             // open the handle to the device
  198.             GetCurrentDirectory( sizeof Path, Path );
  199.             wsprintf( Path+lstrlen(Path), "\\%s", VXD_FILE );
  200.             // connect with VxD
  201.             //VxDHandle = CreateFile( Path, 0, 0, NULL,
  202.             VxDHandle = CreateFile( VXD_FILE, 0, 0, NULL,
  203.                             0, FILE_FLAG_OVERLAPPED|
  204.                             FILE_FLAG_DELETE_ON_CLOSE,
  205.                             NULL );
  206.             if ( VxDHandle == INVALID_HANDLE_VALUE )  {
  207.                 wsprintf( msgbuf, "%s is not loaded properly.", VXD_NAME );
  208.                 Abort( hWnd, msgbuf );
  209.                 return FALSE;
  210.             }
  211.  
  212.             // Have driver zero information
  213.             if ( ! DeviceIoControl(    VxDHandle, FILEMON_zerostats,
  214.                                     NULL, 0, NULL, 0, &nb, NULL ) )
  215.             {
  216.                 Abort( hWnd, "Couldn't access device driver") ;
  217.                 return TRUE;
  218.             }
  219.  
  220.             // Start up timer to periodically update screen
  221.             SetTimer( hWnd,    1, 500/*ms*/, NULL );
  222.             
  223.             // Initialization done
  224.             SetCursor( hSaveCursor );
  225.             ReleaseCapture();
  226.             return 0;
  227.  
  228.         case WM_NOTIFY:
  229.             // Make sure its intended for us
  230.             if ( wParam == ID_LIST )  {
  231.                 NM_LISTVIEW    * pNm = (NM_LISTVIEW *)lParam;
  232.                 switch ( pNm->hdr.code )  {
  233.  
  234.                     case LVN_BEGINLABELEDIT:
  235.                         // Don't allow editing of information
  236.                         return TRUE;
  237.                 }
  238.             }
  239.             return 0;
  240.  
  241.         case WM_COMMAND:
  242.  
  243.             switch ( LOWORD( wParam ) )     {
  244.  
  245.                 // stats related commands to send to driver
  246.                 case IDM_CLEAR:
  247.                     // Have driver zero information
  248.                     if ( ! DeviceIoControl(    VxDHandle, FILEMON_zerostats,
  249.                                             NULL, 0, NULL, 0, &nb, NULL ) )
  250.                     {
  251.                         Abort( hWnd, "Couldn't access device driver");
  252.                         return TRUE;
  253.                     }
  254.                     // Update statistics windows
  255.                     UpdateStatistics( hWnd, hWndList, TRUE );
  256.                     return 0;
  257.  
  258.                 case IDM_CAPTURE:
  259.                     // Read statistics from driver
  260.                     Capture = !Capture;
  261.                     CheckMenuItem( GetMenu(hWnd), IDM_CAPTURE,
  262.                                     MF_BYCOMMAND|(Capture?MF_CHECKED:MF_UNCHECKED) ); 
  263.                     return 0;
  264.  
  265.                 case IDM_AUTOSCROLL:
  266.                     Autoscroll = !Autoscroll;
  267.                     CheckMenuItem( GetMenu(hWnd), IDM_AUTOSCROLL,
  268.                                     MF_BYCOMMAND|(Autoscroll?MF_CHECKED:MF_UNCHECKED) ); 
  269.                     return 0;
  270.  
  271.                 case IDM_EXIT:
  272.                     // Close ourself
  273.                     SendMessage( hWnd, WM_CLOSE, 0, 0 );
  274.                     return 0;
  275.  
  276.                 case IDM_SAVE:
  277.                     SaveFile( hWnd, hWndList, FALSE );
  278.                     return 0;
  279.  
  280.                 case IDM_SAVEAS:
  281.                     SaveFile( hWnd, hWndList, TRUE );
  282.                     return 0;
  283.  
  284.                 case IDM_ABOUT:
  285.                     // Show the names of the authors
  286.                     DialogBox( hInst, "AboutBox", hWnd, (DLGPROC)About );
  287.                     return 0;
  288.  
  289.                 default:
  290.                     // Default behavior
  291.                     return DefWindowProc( hWnd, message, wParam, lParam );
  292.             }
  293.             break;
  294.  
  295.         case WM_TIMER:
  296.             // Time to query the device driver for more data
  297.             if ( Capture )  {
  298.                 for (;;)  {
  299.                     // Have driver fill Stats buffer with information
  300.                     if ( ! DeviceIoControl(    VxDHandle, FILEMON_getstats,
  301.                                             NULL, 0, &Stats, sizeof Stats,
  302.                                             &StatsLen, NULL ) )
  303.                     {
  304.                         Abort( hWnd, "Couldn't access device driver" );
  305.                         return TRUE;
  306.                     }
  307.                     if ( StatsLen == 0 )
  308.                         break;
  309.                     // Update statistics windows
  310.                     UpdateStatistics( hWnd, hWndList, FALSE );
  311.                 }
  312.             }
  313.             return 0;
  314.  
  315.         case WM_SIZE:
  316.             // Move or resize the List
  317.             MoveWindow( hWndList, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE );
  318.             return 0;
  319.  
  320.         case WM_CLOSE:
  321.             //PostQuitMessage( 0 );
  322.             CloseHandle( VxDHandle );
  323.             return DefWindowProc( hWnd, message, wParam, lParam );
  324.  
  325.         case WM_DESTROY:
  326.             PostQuitMessage(0);
  327.             return 0;
  328.  
  329.         case WM_PAINT:
  330.             if( Deleting ) {
  331.                 hDC = BeginPaint( hWnd, &Paint );
  332.                 EndPaint( hWnd, &Paint );
  333.                 return 1;
  334.             }
  335.             return DefWindowProc( hWnd, message, wParam, lParam );
  336.  
  337.         default:
  338.             // Default behavior
  339.             return DefWindowProc( hWnd, message, wParam, lParam );
  340.     }
  341.     return 0;
  342. }
  343.  
  344.  
  345. /******************************************************************************
  346. *
  347. *    FUNCTION:    Split
  348. *
  349. *    PURPOSE:    Split a delimited line into components
  350. *
  351. ******************************************************************************/
  352. int Split( char * line, char delimiter, char * items[] )
  353. {
  354.     int        cnt = 0;
  355.  
  356.     for (;;)  {
  357.         // Add prefix to list of components        
  358.         items[cnt++] = line;
  359.  
  360.         // Check for more components
  361.         line = strchr( line, delimiter );
  362.         if ( line == NULL )
  363.             return cnt;
  364.  
  365.         // Terminate previous component and move to next
  366.         *line++ = '\0';
  367.     }        
  368. }
  369.  
  370.  
  371. /******************************************************************************
  372. *
  373. *    FUNCTION:    ListAppend
  374. *
  375. *    PURPOSE:    Add a new line to List window
  376. *
  377. ******************************************************************************/
  378. BOOL List_Append( HWND hWndList, DWORD seq, char * line )
  379. {
  380.     LV_ITEM        lvI;    // list view item structure
  381.     int            row;
  382.     char    *    items[20];
  383.     int            itemcnt = 0;
  384.  
  385.     // Split line into columns
  386.     itemcnt = Split( line, '\t', items );
  387.     if ( itemcnt == 0 )
  388.         return TRUE;
  389.  
  390.     // Determine row number for request
  391.     if ( *items[0] )  {
  392.         // Its a new request.  Put at end.
  393.         row = 0x7FFFFFFF;
  394.     } else {
  395.         // Its a status.  Locate its associated request.
  396.         lvI.mask = LVIF_PARAM;
  397.         lvI.iSubItem = 0;
  398.         for ( row = ListView_GetItemCount(hWndList) - 1; row >= 0; --row )  {
  399.             lvI.iItem = row;
  400.             if ( ListView_GetItem( hWndList, &lvI )  &&  (DWORD)lvI.lParam == seq )
  401.                 break;
  402.         }
  403.         if ( row == -1 )
  404.             // No request associated with status.
  405.             return TRUE;
  406.     }
  407.  
  408.     // Sequence number if a new item
  409.     if ( *items[0] )  {
  410.         wsprintf( msgbuf, "%d", seq );
  411.         lvI.mask        = LVIF_TEXT | LVIF_PARAM;
  412.         lvI.iItem        = row;
  413.         lvI.iSubItem    = 0;
  414.         lvI.pszText        = msgbuf;
  415.         lvI.cchTextMax    = lstrlen( lvI.pszText ) + 1;
  416.         lvI.lParam        = seq;
  417.         row = ListView_InsertItem( hWndList, &lvI );
  418.         if ( row == -1 )  {
  419.             wsprintf( msgbuf, "Error adding item %d to list view", seq );
  420.             MessageBox( hWndList, msgbuf, "filemon Error", MB_OK );
  421.             return FALSE;
  422.         }
  423.     }
  424.  
  425.     // Process Name
  426.     if ( itemcnt>0 && *items[0] )  {
  427.         OemToChar( items[0], msgbuf );
  428.         ListView_SetItemText( hWndList, row, 1, msgbuf );
  429.     }
  430.  
  431.     // Request Type
  432.     if ( itemcnt>1 && *items[1] )  {
  433.         OemToChar( items[1], msgbuf );
  434.         ListView_SetItemText( hWndList, row, 2, msgbuf );
  435.     }
  436.  
  437.     // Path 
  438.     if ( itemcnt>3 && *items[2] )  {
  439.         OemToChar( items[2], msgbuf );
  440.         ListView_SetItemText( hWndList, row, 3, msgbuf );
  441.     }
  442.  
  443.     // Result
  444.     if ( itemcnt>2 && *items[4] )  {
  445.         OemToChar( items[4], msgbuf );
  446.         ListView_SetItemText( hWndList, row, 4, msgbuf );
  447.     }
  448.  
  449.     // Additional
  450.     if ( itemcnt>3 && *items[3] )  {
  451.         OemToChar( items[3], msgbuf );
  452.         ListView_SetItemText( hWndList, row, 5, msgbuf );
  453.     }
  454.  
  455.     return TRUE;
  456. }
  457.  
  458.  
  459. /******************************************************************************
  460. *
  461. *    FUNCTION:    UpdateStatistics
  462. *
  463. *    PURPOSE:    Clear the statistics window and refill it with the current 
  464. *                contents of the statistics buffer.  Does not refresh the 
  465. *                buffer from the device driver.
  466. *
  467. ******************************************************************************/
  468. void UpdateStatistics( HWND hWnd, HWND hWndList, BOOL Clear )
  469. {
  470.     PENTRY    ptr;
  471.     int        totitems, i;
  472.  
  473.     // Just return if nothing to do
  474.     if ( !Clear  &&  StatsLen < sizeof(int)+2 )
  475.         return;
  476.  
  477.     // Start with empty list
  478.     if ( Clear ) {
  479.         // post hourglass icon
  480.         SetCapture(hWnd);
  481.         hSaveCursor = SetCursor(hHourGlass);
  482.         
  483.         // Don't do this because ListView controls under Win95 have
  484.         // a bug that makes it take sometimes several minutes to 
  485.         // delete all the items
  486.         //    ListView_DeleteAllItems( hWndList );
  487.         totitems = ListView_GetItemCount( hWndList );
  488.         Deleting = TRUE;
  489.         for(i = 0; i < totitems; i++)
  490.             ListView_DeleteItem( hWndList, 0 );
  491.         Deleting = FALSE;
  492.  
  493.         // Delete done
  494.         SetCursor( hSaveCursor );
  495.         ReleaseCapture();
  496.     }
  497.  
  498.  
  499.     // Add all List items from Stats[] data
  500.     for ( ptr = (void *)Stats; (char *)ptr < Stats+StatsLen; )  {
  501.          // Add to list
  502.         ULONG len = strlen(ptr->text);
  503.         List_Append( hWndList, ptr->seq, ptr->text );
  504.         ptr = (void *)(ptr->text + len + 1);
  505.     }
  506.  
  507.     // Empty the buffer
  508.     StatsLen = 0;
  509.  
  510.     // Scroll so newly added items are visible
  511.     if ( Autoscroll ) 
  512.         ListView_EnsureVisible( hWndList, ListView_GetItemCount(hWndList)-1, FALSE ); 
  513. }
  514.  
  515.  
  516. /****************************************************************************
  517. *    FUNCTION: CreateListView(HWND)
  518. *
  519. *    PURPOSE:  Creates the statistics list view window and initializes it
  520. *
  521. ****************************************************************************/
  522. HWND CreateList( HWND hWndParent )                                     
  523. {
  524.     HWND        hWndList;              // handle to list view window
  525.     RECT        rc;                   // rectangle for setting size of window
  526.     LV_COLUMN    lvC;                // list view column structure
  527.     DWORD        j;
  528.     static struct {
  529.         TCHAR *    Label;    // title of column
  530.         DWORD    Width;    // width of column in pixels
  531.         DWORD    Fmt;
  532.     } column[] = {
  533.         {    "#"            ,    35        },
  534.         {    "Process"    ,    100        },        
  535.         {    "Request"    ,    120        },
  536.         {    "Path"        ,    200        },
  537.         {    "Result"    ,    70        },
  538.         {    "Other"        ,    170        },
  539.     };
  540.  
  541.     // Ensure that the common control DLL is loaded.
  542.     InitCommonControls();
  543.  
  544.     // Get the size and position of the parent window.
  545.     GetClientRect( hWndParent, &rc );
  546.  
  547.     // Create the list view window
  548.     hWndList = CreateWindowEx( 0L, WC_LISTVIEW, "", 
  549.                                 WS_VISIBLE | WS_CHILD | WS_BORDER | LVS_REPORT |
  550.                                     WS_EX_CLIENTEDGE,    // styles
  551.                                 0, 0, rc.right - rc.left, rc.bottom - rc.top,
  552.                                 hWndParent,    (HMENU)ID_LIST, hInst, NULL );
  553.     if ( hWndList == NULL )
  554.         return NULL;
  555.  
  556.     // Initialize columns
  557.     lvC.mask    = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
  558.     lvC.fmt        = LVCFMT_LEFT;    // left-align column
  559.  
  560.     // Add the columns.
  561.     for ( j = 0; j < sizeof column/sizeof column[0]; ++j )  {
  562.         lvC.iSubItem    = j;
  563.         lvC.cx            = column[j].Width;
  564.          lvC.pszText        = column[j].Label;
  565.         if ( ListView_InsertColumn( hWndList, j, &lvC ) == -1 )
  566.             return NULL;
  567.     }
  568.  
  569.     return hWndList;
  570. }
  571.  
  572.  
  573. /****************************************************************************
  574. *    FUNCTION: SaveFile()
  575. *
  576. *    PURPOSE:  Lets the user go select a file.
  577. *
  578. ****************************************************************************/
  579. void SaveFile( HWND hWnd, HWND ListBox, BOOLEAN SaveAs )
  580. {
  581.     OPENFILENAME    SaveFileName;
  582.     char            szFile[256] = "", fieldtext[256], output[1024];
  583.     FILE            *hFile;
  584.     int                numitems;
  585.     int                row, subitem;
  586.  
  587.     if( SaveAs || !FileChosen ) {
  588.         SaveFileName.lStructSize       = sizeof (SaveFileName);
  589.         SaveFileName.hwndOwner         = hWnd;
  590.         SaveFileName.hInstance         = (HANDLE) hInst;
  591.         SaveFileName.lpstrFilter       = "File Data (*.FIL)\0*.FIL\0All (*.*)\0*.*\0";
  592.         SaveFileName.lpstrCustomFilter = (LPTSTR)NULL;
  593.         SaveFileName.nMaxCustFilter    = 0L;
  594.         SaveFileName.nFilterIndex      = 1L;
  595.         SaveFileName.lpstrFile         = szFile;
  596.         SaveFileName.nMaxFile          = 256;
  597.         SaveFileName.lpstrFileTitle    = NULL;
  598.         SaveFileName.nMaxFileTitle     = 0;
  599.         SaveFileName.lpstrInitialDir   = NULL;
  600.         SaveFileName.lpstrTitle        = "Save File Info...";
  601.         SaveFileName.nFileOffset       = 0;
  602.         SaveFileName.nFileExtension    = 0;
  603.         SaveFileName.lpstrDefExt       = "*.fil";
  604.         SaveFileName.lpfnHook           = NULL;
  605.          SaveFileName.Flags = OFN_LONGNAMES|OFN_HIDEREADONLY;
  606.  
  607.         if( !GetSaveFileName( &SaveFileName )) 
  608.             return;
  609.     } else 
  610.         // open previous szFile
  611.         strcpy( szFile, szFileName );
  612.  
  613.     // open the file
  614.     hFile = fopen( szFile, "w" );
  615.     if( !hFile ) {
  616.         MessageBox(    NULL, "Create File Failed.",
  617.                 "Save Error", MB_OK|MB_ICONSTOP );
  618.         return;
  619.     }
  620.     // post hourglass icon
  621.     SetCapture(hWnd);
  622.     hSaveCursor = SetCursor(hHourGlass);
  623.  
  624.     numitems = ListView_GetItemCount(ListBox);
  625.     for ( row = 0; row < numitems; row++ )  {
  626.         output[0] = 0;
  627.         for( subitem = 0; subitem < 6; subitem++ ) {
  628.             fieldtext[0] = 0;
  629.             ListView_GetItemText( ListBox, row, subitem, fieldtext, 256 );
  630.             strcat( output, fieldtext );
  631.             strcat( output, "\t" );
  632.         }
  633.         fprintf( hFile, "%s\n", output );
  634.     }
  635.     fclose( hFile );
  636.     strcpy( szFileName, szFile );
  637.     FileChosen = TRUE;
  638.     SetCursor( hSaveCursor );
  639.     ReleaseCapture(); 
  640. }
  641.  
  642.  
  643. /****************************************************************************
  644. *
  645. *    FUNCTION:    About
  646. *
  647. *    PURPOSE:    Processes messages for "About" dialog box
  648. *
  649. ****************************************************************************/
  650.  
  651. BOOL APIENTRY About( HWND hDlg, UINT message, UINT wParam, LONG lParam )
  652. {
  653.     switch ( message )  {
  654.        case WM_INITDIALOG:
  655.           return TRUE;
  656.  
  657.        case WM_COMMAND:              
  658.           if ( LOWORD( wParam ) == IDOK )     {
  659.               EndDialog( hDlg, TRUE );
  660.               return TRUE;
  661.           }
  662.           break;
  663.     }
  664.     return FALSE;   
  665. }
  666.